package main

import (
	"bytes"
	"encoding/binary"
	"log"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

//HTTP echo server
func HTTPServer() {
	port := 8989
	log.Println("starting server, listening on port " + strconv.Itoa(port))
	http.HandleFunc("/random", randomHandler)
	http.HandleFunc("/list", listHandler)
	http.HandleFunc("/ping", pingHandler)
	http.ListenAndServe(":"+strconv.Itoa(port), nil)
}

// EchoHandler echos back the request as a response
func randomHandler(writer http.ResponseWriter, r *http.Request) {
	writer.Header().Set("Access-Control-Allow-Origin", "*")
	writer.Header().Set("Access-Control-Allow-Headers", "Content-Range, Content-Disposition, Content-Type, ETag")
	var networkType string
	u, err := url.Parse(r.URL.String())
	ErrorHandle(err, writer, r)
	values, err := url.ParseQuery(u.RawQuery)
	ErrorHandle(err, writer, r)
	if len(values["network"]) == 0 {
		networkType = ""
	} else {
		networkType = values["network"][0]
	}
	// fmt.Println(values)
	result := peers.GetRandom(networkType)
	if result != nil {
		writer.Write([]byte(result.Ip + ":" + strconv.Itoa(result.Port)))
		// fmt.Fprintf(writer, "%s", request.RemoteAddr)
	} else {
		writer.Write([]byte(""))
	}
}

// EchoHandler echos back the request as a response
func listHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Access-Control-Allow-Origin", "*")
	writer.Header().Set("Access-Control-Allow-Headers", "Content-Range, Content-Disposition, Content-Type, ETag")
	// writer.Write([]byte(request.RemoteAddr))
	result := peers.ToSlice()
	if len(result) != 0 {
		list := []string{}
		for id, o := range peers.items {
			list = append(list, id+"----"+o.Ip+":"+strconv.Itoa(o.Port))
		}

		writer.Write([]byte(strings.Join(list, "\r\n")))
		// fmt.Fprintf(writer, "%s", request.RemoteAddr)
	} else {
		writer.Write([]byte(""))
	}
}
func pingHandler(writer http.ResponseWriter, r *http.Request) {
	writer.Header().Set("Access-Control-Allow-Origin", "*")
	writer.Header().Set("Access-Control-Allow-Headers", "Content-Range, Content-Disposition, Content-Type, ETag")
	var peer Peer
	u, err := url.Parse(r.URL.String())
	ErrorHandle(err, writer, r)
	values, err := url.ParseQuery(u.RawQuery)
	ErrorHandle(err, writer, r)
	if len(values["network"]) > 0 {
		peer.Network = values["network"][0]
	}
	if len(values["port"]) > 0 {
		i, _ := strconv.Atoi(values["port"][0])
		peer.Port = i
	}
	if len(values["version"]) > 0 {
		i, _ := strconv.Atoi(values["version"][0])
		peer.Version = i
	}
	if len(values["protocol"]) > 0 {
		peer.Protocol = values["protocol"][0]
	}
	if len(values["id"]) > 0 {
		peer.Id = values["id"][0]
	}
	peer.Ip = RemoteIp(r)
	if peer.Port == 0 || peer.Ip == "" || peer.Protocol == "" {
		writer.Write([]byte("Error"))
		return
	}
	peers.Set(&peer)
	writer.Write([]byte("OK"))
}
func ErrorHandle(err error, writer http.ResponseWriter, r *http.Request) {
	if err != nil {
		writer.Write([]byte(""))
	}
}

//ipRange - a structure that holds the start and end of a range of ip addresses
type ipRange struct {
	start net.IP
	end   net.IP
}

// inRange - check to see if a given ip address is within a range given
func inRange(r ipRange, ipAddress net.IP) bool {
	// strcmp type byte comparison
	if bytes.Compare(ipAddress, r.start) >= 0 && bytes.Compare(ipAddress, r.end) < 0 {
		return true
	}
	return false
}

var privateRanges = []ipRange{
	ipRange{
		start: net.ParseIP("10.0.0.0"),
		end:   net.ParseIP("10.255.255.255"),
	},
	ipRange{
		start: net.ParseIP("100.64.0.0"),
		end:   net.ParseIP("100.127.255.255"),
	},
	ipRange{
		start: net.ParseIP("172.16.0.0"),
		end:   net.ParseIP("172.31.255.255"),
	},
	ipRange{
		start: net.ParseIP("192.0.0.0"),
		end:   net.ParseIP("192.0.0.255"),
	},
	ipRange{
		start: net.ParseIP("192.168.0.0"),
		end:   net.ParseIP("192.168.255.255"),
	},
	ipRange{
		start: net.ParseIP("198.18.0.0"),
		end:   net.ParseIP("198.19.255.255"),
	},
}

// isPrivateSubnet - check to see if this ip is in a private subnet
func isPrivateSubnet(ipAddress net.IP) bool {
	// my use case is only concerned with ipv4 atm
	if ipCheck := ipAddress.To4(); ipCheck != nil {
		// iterate over all our ranges
		for _, r := range privateRanges {
			// check if this ip is in a private range
			if inRange(r, ipAddress) {
				return true
			}
		}
	}
	return false
}

const (
	XForwardedFor = "X-Forwarded-For"
	XRealIP       = "X-Real-IP"
)

// RemoteIp 返回远程客户端的 IP，如 192.168.1.1
func RemoteIp(req *http.Request) string {
	remoteAddr := req.RemoteAddr
	if ip := req.Header.Get(XRealIP); ip != "" {
		remoteAddr = ip
	} else if ip = req.Header.Get(XForwardedFor); ip != "" {
		remoteAddr = ip
	} else {
		remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
	}

	if remoteAddr == "::1" {
		remoteAddr = "127.0.0.1"
	}
	remote := net.ParseIP(remoteAddr)
	if !remote.IsGlobalUnicast() || isPrivateSubnet(remote) {
		return ""
	}
	return remoteAddr
}

// Ip2long 将 IPv4 字符串形式转为 uint32
func Ip2long(ipstr string) uint32 {
	ip := net.ParseIP(ipstr)
	if ip == nil {
		return 0
	}
	ip = ip.To4()
	return binary.BigEndian.Uint32(ip)
}
